/*
 * Author: Benedikt Sauter
 * Author: Peter Huewe, Hubert Hogel
 * All rights reserved.
 *
 * Short descripton of file:
 *
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution.
 *   * Neither the name of the FH Augsburg nor the names of its 
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdint.h>

#include "main.h"
#include "common.h"
#include "protocol.h"
#include "i2c.h"

static uint8_t i2c_get_twsr (void)
{
	return (TWSR & 0xF8);
}

static void i2c_wait_for_complete (void)
{
	while (!(TWCR & (1 << TWINT)))
	{
		//do nothing just wait
		;
	}
}

static uint8_t i2c_init (void)
{
	TWCR |= (1 << TWEN);
	octopus.ports[12] = PIN_SCL;
	octopus.ports[13] = PIN_SDA;
	return RSP_OK;
}

static uint8_t i2c_deinit (void)
{
	TWCR &= ~(1 << TWEN);		// deinit
	octopus.ports[12] = PIN_OUT;	// this is wrong!
	octopus.ports[13] = PIN_OUT;	// this is wrong!
	return RSP_OK;
}

static uint8_t i2c_set_bitrate (int speed)
{
	if (speed == 1)
		//TWBR = 12;  // 400 kHz
		TWBR = (uint8_t) ((F_OSC / 400000UL - 16) / 2);
	else if (speed == 2)
		TWBR = (uint8_t) ((F_OSC / 250000UL - 16) / 2);
	else if (speed == 3)
		TWBR = 72;				// 100kHz
	//TWBR = (F_OSC / 100000UL - 16)/2; 
	else
	{
		return RSP_ERROR;		// unkown speed
	}
	//TWBR = 72;  // 100kHz
	TWSR &= ~((1 << TWPS1) | (1 << TWPS0));
	TWSR |= (0 << TWPS0);		//buf[4]
	return RSP_OK;
}

static uint8_t i2c_send_start (void)
{
	i2c_get_twsr ();
	TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
	i2c_wait_for_complete ();
	return RSP_OK;
}

static uint8_t i2c_send_stop (void)
{
	TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
	return RSP_OK;
}

static uint8_t i2c_receive_byte (void)
{
	TWCR = (1 << TWINT) | (1 << TWEN);
	i2c_wait_for_complete ();
	return TWDR;
}

static void i2c_send_byte (uint8_t buf)
{
	TWDR = buf;
	TWCR = (1 << TWINT) | (1 << TWEN);
	i2c_wait_for_complete ();
}


static void i2c_init_usb (void)
{
	answer[0] = CMD_I2C_INIT;
	answer[1] = i2c_init ();
	answer[2] = 0;
	CommandAnswer (3);
}

static void i2c_deinit_usb (void)
{
	answer[0] = CMD_I2C_DEINIT;
	answer[1] = i2c_deinit ();
	answer[2] = 0;
	CommandAnswer (3);
}

static void i2c_set_bitrate_usb (int speed)
{
	answer[0] = CMD_I2C_BITRATE;
	answer[1] = i2c_set_bitrate (speed);
	answer[2] = 0;
	CommandAnswer (3);
}

static void i2c_send_start_usb (void)
{
	answer[0] = CMD_I2C_SEND_START;
	answer[1] = i2c_send_start ();
	answer[2] = 0;
	CommandAnswer (3);
}

static void i2c_send_stop_usb (void)
{
	answer[0] = CMD_I2C_SEND_STOP;
	answer[1] = i2c_send_stop ();
	answer[2] = 0;
	CommandAnswer (3);
}

static void i2c_send_bytes_usb (uint8_t address, uint8_t len, uint8_t timeout, uint8_t *data)
{
	//i2c_send_byte((char)address << 1);  /* (00010000<<1) | W */

	while (len--)
		i2c_send_byte (*data++);

	answer[0] = CMD_I2C_SEND_BYTES;
	answer[1] = RSP_OK;
	answer[2] = 0;
	CommandAnswer (3);
}

static void i2c_reveice_bytes_usb (int address, int len, int timeout)
{
	/* receive */
	//i2c_get_twsr(); 
	//print_hex(s);

	//i2c_send_start();
	/* slave address = 0x10; */
	//buf[3] = (0x10 << 1)|1;  /* (00010000<<1) | R */
	//i2c_send_byte( (0x10 << 1)|1);

	answer[0] = CMD_I2C_RECV_BYTES;
	answer[1] = RSP_OK;
	answer[2] = 1;
	answer[3] = i2c_receive_byte ();
	CommandAnswer (5);
}

void i2c_parser (uint8_t *buf)
{
	switch (buf[0])
	{
	case CMD_I2C_INIT:
		i2c_init_usb ();
		break;
	case CMD_I2C_DEINIT:
		i2c_deinit_usb ();
		break;
	case CMD_I2C_BITRATE:
		i2c_set_bitrate_usb (buf[2]);
		break;
	case CMD_I2C_SEND_BYTES:
		//void i2c_send_bytes_usb(int address,int len, int timeout,char * data)
		i2c_send_bytes_usb (buf[3], buf[1] - 4, buf[2], &buf[4]);
		break;
	case CMD_I2C_RECV_BYTES:
		i2c_reveice_bytes_usb (buf[3], buf[1] - 4, buf[2]);
		break;
	case CMD_I2C_SEND_START:
		i2c_send_start_usb ();
		break;
	case CMD_I2C_SEND_STOP:
		i2c_send_stop_usb ();
		break;
	default:
		answer[0] = buf[0];
		answer[1] = RSP_UNKOWN_CMD;
		answer[2] = 0;
		CommandAnswer (3);
	}
}
